import UAParser from "ua-parser-js"
import {
    createDefaultOption,
    LogCollectOption,
    LogEvent,
    LogEventType,
    ModeType,
    StorageData,
    UserData,
} from "model/logCollect.model"
import { DBType, SAEvents } from "model/siteAnalysis.model"
import { guid, IS_LOCAL } from "./utils/utils"
import * as Package from "../package.json"
import * as EventEmitter from "events"
// @ts-ignore
import { interval, Subscription } from "rxjs"
// @ts-ignore
import { filter } from "rxjs/operators"
import Worker from "./utils/logServer.worker.js"
import { print } from "./utils/print"
import { httpClient } from "./utils/httpClient"
import { crudFromTable, INIT_LOG_TABLE_WEBSQL } from "utils/logWebsql"
import { ADD_ITEM_DB, DEL_ITEM_BYID, GET_ALL_ITEM, OPEN_DB_AND_INIT } from "utils/logIndexDB"
import { ACEvents } from "./model/authClient.model"

const { version } = Package
const SINGLE_NAME = 'jxstjh_log_collect'
declare const window: any

class LogCollect {
    id: string
    e: any
    _db: any
    _prod: boolean
    _version: string
    _ip: string
    _dbType: DBType
    _UA: any
    private _userData: UserData
    private set userData(userData) {
        this._userData = userData
    }
    private get userData() {
        return this._userData
    }
    private _worker
    private get db() {
        return this._db
    }
    private set db(db) {
        this._db = db
    }
    private _emitter = new EventEmitter()
    private $upLoadSubction: Subscription
    constructor(public option: LogCollectOption) {
        // 保证全局单例
        if (window[SINGLE_NAME]) {
            return window[SINGLE_NAME]
        } else {
            this.id = 'ac' + guid()
            this._prod = this.option.production
            this._version = version
            if (!option.logServer) {
                setTimeout(() => {
                    this._emitter.emit(SAEvents.ERROR, '上传地址必传')
                }, 0)
            } else if (!option['resourceCode']) {
                setTimeout(() => {
                    this._emitter.emit(SAEvents.ERROR, '资源 code 必传')
                }, 0)
            } else {
                this.option = createDefaultOption(option)
                this._UA = new UAParser().getResult()
                this._worker = new Worker()
                this._worker.onmessage = this.onWorker
                this.getIPbyFront() // 前端获取ip
                this._prod && this.init()
            }
            window.jxstjh_logCollect = this
        }
    }
    private init() {
        const { resourceCode } = this.option
        if (window.openDatabase) {
            this.db = window.openDatabase('LOG' + resourceCode, '1.0', 'JXSTJH_LOG_COLLECT', 48 * 1024 * 1024)
            this._dbType = 'webSQL'
            INIT_LOG_TABLE_WEBSQL(this.db).catch(err => {
                this._emitter.emit(SAEvents.ERROR, err)
            }).then(() => {
                //this.insetLogMsg()
            })
        } else if (window.indexedDB) {
            OPEN_DB_AND_INIT('LOG' + resourceCode)
                .then((e) => {
                    this.db = e.target.result
                    this._dbType = 'indexDB'
                    return true
                })
                .then(() => {
                    //this.insetLogMsg()
                })
        } else {
            setTimeout(() => {
                this._emitter.emit(SAEvents.ERROR, '不支持webSQL')
                print('JH_LOG', '不支持webSQL!', 'error')
            }, 0)
        }
        if (false) {
            // TODO 升级逻辑
            //DROP_TABLE(this.db, 'LOG_TABLE')
        }
        this.$upLoadSubction = interval(this.option.interval * 1000).pipe(filter(() => (!!this.db && this._prod))).subscribe(() => {
            this.sqlUnLoadLog()
                .then(row => this.upLoadLog(row))
                .catch(() => { })
        })
    }
    public recordPage() {
        if (this._prod) {
            return this.insetLogMsg({ event: LogEventType.PAGE })
        } else {
            return Promise.resolve('recordPage in production only')
        }
    }
    private getIPbyFront() {
        const id = 'log-sohuIp'
        if (document.getElementById(id)) {
            return
        }
        const s = document.createElement('script')
        s.id = id
        s.src = 'http://pv.sohu.com/cityjson?ie=utf-8'
        s.onload = () => {
            this._ip = window.returnCitySN && window.returnCitySN.cip
        }
        document.body.appendChild(s)
    }
    public async setUser(umsAdmin: any) {
        await httpClient(this.option.logServer + '/api/public/simple-domain', {
            deptId: umsAdmin.officeId,
            resourceCode: this.option.resourceCode,
            deptName: '',
            resourceName: ''
        }, 'POST').then((res) => {
            if (res && res.code - 0 === 0) {
                // createBy, deptId 存在超出安全范围限制，建议 string
                this.userData = {
                    createBy: '' + umsAdmin.id,
                    deptId: '' + res.data.deptId,
                    deptName: res.data.deptName,
                    userName: umsAdmin.loginName,
                    resourceName: res.data.resourceName,
                    photoLarge: umsAdmin.photo,
                }
                //this.insetLogMsg({ event: LogEventType.PAGE })
            } else {
                this._emitter.emit(ACEvents.ERROR, res)
                this.destroy()
            }
        }).catch((e) => {
            this._emitter.emit(ACEvents.ERROR, e)
            this.destroy()
        })
    }
    public recordEvent(eventName: string, eventObject?: string) {
        this._prod && this.insetLogMsg({ event: LogEventType.FUNCTION, eventName, eventObject })
    }
    private insetLogMsg(logEvent: LogEvent) {
        return new Promise((reslove, reject) => {
            if (IS_LOCAL(location.href) && !this.option.production) {
                reslove('本地环境')
                return
            }

            let data: StorageData = {
                resourceName: this.userData.resourceName,
                createBy: this.userData.createBy,                // 用户id
                deptId: this.userData.deptId,                    // 主部门id
                deptName: this.userData.deptName,                // 主部门名称
                userName: this.userData.userName,                // 用户姓名
                photoLarge: this.userData.photoLarge,            // 头像
                event: logEvent.event,                           // 事件类型
                eventName: logEvent.eventName || '',             // 事件组件名称
                eventObject: logEvent.eventObject || '',         // 事件对象
                resourceCode: this.option.resourceCode,          // 必填
                createDate: new Date().getTime(),
                ip: this._ip || '',
                resolvingPower: window.screen.width + '*' + window.screen.height,
                route: location.href,
                titleName: document.title,
                equipment: this._UA.device.vendor || '',
                browserType: this._UA.browser.name || '',
                operatingSystem: this._UA.os.name || '',
                osVersion: this._UA.os.version || '',
                mode: this._UA.device.type ? ModeType.APP : ModeType.PC,
                UA: window.navigator.userAgent || '',
                uId: guid(),                                // 唯一标识（本地）
                version: version,                           // 版本（本地）
                is_upload: 0,                               // 是否上传（本地）
            }
            if (this._dbType === 'webSQL') {
                const sqlstr = `INSERT INTO LOG_COLLECT (createBy, deptId, deptName, userName, event, eventName, resourceCode, resourceName, equipment, browserType, createDate, ip, operatingSystem, resolvingPower, route, titleName, UA, uId, version, is_upload, mode, osVersion, eventObject, photoLarge) VALUES ('${data.createBy}','${data.deptId}','${data.deptName}','${data.userName}','${data.event}','${data.eventName}','${data.resourceCode}','${data.resourceName}','${data.equipment}','${data.browserType}','${data.createDate}','${data.ip}','${data.operatingSystem}','${data.resolvingPower}','${data.route}','${data.titleName}','${data.UA}','${data.uId}','${data.version}','${data.is_upload}', '${data.mode}', '${data.osVersion}', '${data.eventObject}', '${data.photoLarge}')`
                crudFromTable(this.db, sqlstr).then(() => {
                    reslove('插入数据成功!')
                }).catch(err => {
                    reject(err)
                })
            } else if (this._dbType === 'indexDB') {
                ADD_ITEM_DB(this.db, data).then(e => {
                    reslove(e)
                }).catch(err => {
                    reject(err)
                })
            }
        })
    }
    private sqlUnLoadLog = () => {
        if (this._dbType === 'webSQL') {
            return new Promise((reslove, reject) => {
                const sqlStr = 'SELECT * FROM LOG_COLLECT WHERE is_upload="0"'
                crudFromTable(this.db, sqlStr).then((res) => {
                    const _res = Array.prototype.slice.call(res.rows)
                    if (Array.isArray(_res) && _res.length > 0) {
                        reslove(_res)
                    } else {
                        reject([])
                    }
                }).catch(err => {
                    reject(err)
                })
            })
        } else if (this._dbType === 'indexDB') {
            return new Promise((reslove, reject) => {
                GET_ALL_ITEM(this.db).then(row => {
                    reslove(row)
                }).catch(err => {
                    reject(err)
                })
            })
        }
    }
    private upLoadLog(row) {
        return new Promise((reslove) => {
            if (Array.isArray(row) && row.length > 0) {
                this._worker.postMessage({ cmd: 'UPLOADLOGLIST', url: this.option.logServer, row })
                reslove(true)
            }
        })
    }
    private onWorker = (e) => {
        switch (e.data.cmd) {
            case 'UPLOAD_SUCCESS':
                const ids = e.data.ids
                this.upLoaderServerSuccess(ids)
                break
            default:
                break
        }
    }
    private upLoaderServerSuccess(ids) {
        print('JH_SA', 'upLoaderServerSuccess!', 'success')
        if (this._dbType === 'webSQL') {
            return new Promise((reslove, reject) => {
                if (ids.length < 1) {
                    reject('length 0')
                }
                let sqlStr = `DELETE FROM LOG_COLLECT WHERE uId in (`
                ids.forEach((id, index) => {
                    if (index === ids.length - 1) {
                        sqlStr += `'${id}'`

                    } else {
                        sqlStr += `'${id}',`
                    }
                })
                sqlStr += ')'
                crudFromTable(this.db, sqlStr).then(() => {
                    reslove(true)
                }).catch(err => {
                    reject(err)
                })
            })
        } else if (this._dbType === 'indexDB') {
            if (Array.isArray(ids) && ids.length > 0) {
                ids.forEach(id => DEL_ITEM_BYID(this.db, id))
            }
        }
    }
    on(event, listener) {
        this._emitter.addListener(event, listener)
    }
    public destroy() {
        this.e = null
        this._emitter.removeAllListeners()
        this.$upLoadSubction && this.$upLoadSubction.unsubscribe()
        this._emitter = null
        this._prod = false
    }
}
export default LogCollect